#pragma once

namespace gecko
{

	namespace gtl
	{

		template<typename T>
		List<T>::List()
		{
			m_Head = new Node;
			m_Tail = new Node;

			m_Head->Prev = 0;
			m_Head->Next = m_Tail;

			m_Tail->Prev = m_Head;
			m_Tail->Next = 0;

			m_Count = 0;
		}

		template<typename T>
		List<T>::List(const List& list)
		{
			m_Head = new Node;
			m_Tail = new Node;

			m_Head->Prev = 0;
			m_Head->Next = m_Tail;

			m_Tail->Prev = m_Head;
			m_Tail->Next = 0;

			// copy over data from other list
			for ( ConstIterator itr = list.Begin(); itr != list.End(); ++itr )
			{
				PushBack(*itr);
			}
		}

		template<typename T>
		List<T>::~List()
		{
			Clear();
			delete m_Head;
			delete m_Tail;
		}

		template<typename T>
		List<T>& List<T>::operator=( const List& list )
		{
			m_Head->Prev = 0;
			m_Head->Next = m_Tail;

			m_Tail->Prev = m_Head;
			m_Tail->Next = 0;

			// copy over data from other list
			for ( ConstIterator itr = list.Begin(); itr != list.End(); ++itr )
			{
				PushBack(*itr);
			}

			return *this;
		}


		template<typename T>
		typename List<T>::Iterator List<T>::Begin()
		{
			return (Iterator(m_Head->Next));
		}

		template<typename T>
		typename List<T>::Iterator List<T>::End()
		{
			return (Iterator(m_Tail));
		}

		template<typename T>
		typename List<T>::ConstIterator List<T>::Begin() const
		{
			return (ConstIterator(m_Head->Next));
		}

		template<typename T>
		typename List<T>::ConstIterator List<T>::End() const
		{
			return (ConstIterator(m_Tail));
		}

		template<typename T>
		void List<T>::Insert(const T& data, Iterator before)
		{
			Node* beforeNode = before.m_Current;
			Node* nextNode = beforeNode;
			Node* prevNode = beforeNode->Prev;

			Node* newNode = new Node;
			newNode->Data = data;
			newNode->Next = nextNode;
			newNode->Prev = prevNode;

			prevNode->Next = newNode;
			nextNode->Prev = newNode;

			++m_Count;
		}

		template<typename T>
		void List<T>::Erase(Iterator where)
		{
			Node* toRemove = where.m_Current;
			Node* prevNode = toRemove->Prev;
			Node* nextNode = toRemove->Next;

			// unlink node
			prevNode->Next = nextNode;
			nextNode->Prev = prevNode;

			// delete node
			delete toRemove;


			--m_Count;
		}

		template<typename T>
		void List<T>::Remove(const T& data)
		{
			Iterator where = Find(data);
			if ( where != End() )
			{
				Erase(where);
			}
		}

		template<typename T>
		typename List<T>::Iterator List<T>::Find(const T& data)
		{
			for ( Iterator itr = Begin(); itr != End(); ++itr )
			{
				if ( (*itr) == data )
				{
					return itr;
				}
			}

			return End();
		}

		template<typename T>
		typename List<T>::ConstIterator List<T>::Find(const T& data) const
		{
			for ( ConstIterator itr = Begin(); itr != End(); ++itr )
			{
				if ( (*itr) == data )
				{
					return itr;
				}
			}

			return End();
		}

		template<typename T>
		void List<T>::PushBack(const T& data)
		{
			Insert(data, End());
		}

		template<typename T>
		void List<T>::PushFront(const T& data)
		{
			Insert(data, Begin());
		}

		template<typename T>
		void List<T>::PopFront()
		{
			Erase(Begin());
		}

		template<typename T>
		void List<T>::PopBack()
		{
			Erase(--End());
		}

		template<typename T>
		T& List<T>::Front()
		{
			return m_Head->Next->Data;
		}

		template<typename T>
		const T& List<T>::Front() const
		{
			return m_Head->Next->Data;
		}

		template<typename T>
		T& List<T>::Back()
		{
			return m_Tail->Prev->Data;
		}

		template<typename T>
		const T& List<T>::Back() const
		{
			return m_Tail->Prev->Data;
		}

		template<typename T>
		void List<T>::Clear()
		{
			Node* front = m_Head->Next;

			for ( Node* node = front->Next; node != 0 ; node = node->Next )
			{
				Node* toRemove = node->Prev;
				delete toRemove;
			}

			m_Head->Next = m_Tail;
			m_Tail->Prev = m_Head;

			m_Count = 0;
		}

		template<typename T>
		unsigned int List<T>::Size()
		{
			return m_Count;
		}

		template<typename T>
		bool List<T>::Empty()
		{
			return m_Count == 0;
		}



		namespace internal
		{

			// base class for list iterators
			template<typename ListClass, typename T>
			class ListIterator
			{
			public:
				// Default constructor.
				ListIterator();

				// Construct from node.
				ListIterator(typename ListClass::Node* node);

				// Copy constructor.
				ListIterator(const ListIterator& itr);

				// Pre-increment.
				ListIterator& operator++();

				// Post-increment.
				ListIterator& operator++(int);

				// Pre-decrement.
				ListIterator& operator--();

				// Post-decrement.
				ListIterator& operator--(int);

				// Equality operator.
				bool operator==(const ListIterator& itr) const;

				// Inequality operator.
				bool operator!=(const ListIterator& itr) const;

				// Dereference operator.
				T& operator*() const;

				// Dereference operator.
				T& operator*();

				// pointer to node
				typename ListClass::Node* m_Current;
			};

			template<typename ListClass, typename T>
			ListIterator<ListClass, T>::ListIterator()
				: m_Current(0)
			{
			}

			template<typename ListClass, typename T>
			ListIterator<ListClass, T>::ListIterator(typename ListClass::Node* node)
				: m_Current(node)
			{
			}

			template<typename ListClass, typename T>
			ListIterator<ListClass, T>::ListIterator(const ListIterator& itr)
				: m_Current(itr.m_Current)
			{
			}

			template<typename ListClass, typename T>
			ListIterator<ListClass, T>& ListIterator<ListClass, T>::operator++()
			{	
				m_Current = m_Current->Next;
				return (*this);
			}

			template<typename ListClass, typename T>
			ListIterator<ListClass, T>& ListIterator<ListClass, T>::operator++(int)
			{	
				m_Current = m_Current->Next;
				return (this);
			}

			template<typename ListClass, typename T>
			ListIterator<ListClass, T>& ListIterator<ListClass, T>::operator--()
			{	
				m_Current = m_Current->Prev;
				return (*this);
			}

			template<typename ListClass, typename T>
			ListIterator<ListClass, T>& ListIterator<ListClass, T>::operator--(int)
			{	
				m_Current = m_Current->Prev;
				return (*this);
			}

			template<typename ListClass, typename T>
			bool ListIterator<ListClass, T>::operator==(const ListIterator& itr) const
			{
				return m_Current == itr.m_Current;
			}

			template<typename ListClass, typename T>
			bool ListIterator<ListClass, T>::operator!=(const ListIterator& itr) const
			{
				return m_Current != itr.m_Current;
			}

			template<typename ListClass, typename T>
			T& ListIterator<ListClass, T>::operator*() const
			{
				return m_Current->Data;
			}

			template<typename ListClass, typename T>
			T& ListIterator<ListClass, T>::operator*()
			{
				return m_Current->Data;
			}

		} // namespace internal

	} // namespace gtl

} // namespace gecko